import copy

type_pool = []
registered_prototype = {}


def proto_type_builder(type_name='@Unnamed'):
    if type_name[0] == '$':
        return ProtoType(type_name)
    else:
        return ProtoType(type_name)


class ProtoType:
    __standard_atr = None

    def __init__(self, type_name='@None'):
        self.atr = {
            'clone': self.clone,
            '@type_name': type_name
        }
        if type_name[0] not in ['$', '@']:
            self.type_id = len(type_pool)
            type_pool.append(self)
        else:
            self.type_id = 0

    def get_ele(self, name):
        res = self.atr.get(name, None)
        if res is None and self.atr['@type_name'][0] == '$':
            res = ProtoType.__standard_atr.get(name, None)
        return res

    def set_ele(self, name, value):
        self.atr[name] = value

    def clone(self):
        res = ProtoType()
        res.atr = copy.copy(self.atr)
        return res

    def register(self):
        registered_prototype[self.get_ele('@type_name')] = self.type_id

    def __str__(self):
        data = self.get_ele('@data')
        if data is None:
            return f"type: {self.get_ele('@type_name')}, type_id: {self.type_id}"
        if isinstance(data, list):
            data = [str(_) for _ in data]
        return str(data)

    def copy(self, target):
        self.atr = copy.copy(target.atr)

    @classmethod
    def class_init(cls, standard_atr):
        cls.__standard_atr = standard_atr


def gt(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    res = ProtoType('$bool$')
    res.set_ele('@data', l_v > r_v)
    return res


def lt(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    res = ProtoType('$bool$')
    res.set_ele('@data', l_v < r_v)
    return res


def ge(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    res = ProtoType('$bool$')
    res.set_ele('@data', l_v >= r_v)
    return res


def le(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    res = ProtoType('$bool$')
    res.set_ele('@data', l_v <= r_v)
    return res


def eq(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    res = ProtoType('$bool$')
    res.set_ele('@data', l_v == r_v)
    return res


def __type_to_string(value):
    if isinstance(value, int):
        return '$int$'
    elif isinstance(value, float):
        return '$float$'
    elif isinstance(value, str):
        return '$string$'
    elif isinstance(value, list):
        return '$list$'


def plus(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v + r_v
    res = ProtoType(__type_to_string(value))
    res.set_ele('@data', value)
    return res


def minus(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v - r_v
    res = ProtoType(__type_to_string(value))
    res.set_ele('@data', value)
    return res


def times(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v * r_v
    res = ProtoType(__type_to_string(value))
    res.set_ele('@data', value)
    return res


def divide(l_v, r_v, type_name, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v / r_v
    res = ProtoType(__type_to_string(value))
    res.set_ele('@data', value)
    return res


# 布尔逻辑操作

def and_operation(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v and r_v
    res = ProtoType('$bool$')
    res.set_ele('@data', value)
    return res


def or_operation(l_v, r_v, **kwargs):
    l_v = l_v.get_ele('@data')
    r_v = r_v.get_ele('@data')
    value = l_v or r_v
    res = ProtoType('$bool$')
    res.set_ele('@data', value)
    return res


def not_operation(l_v, **kwargs):
    l_v = l_v.get_ele('@data')
    value = not l_v
    res = ProtoType('$bool$')
    res.set_ele('@data', value)
    return res


def to_int(var: ProtoType):
    try:
        odata = var.get_ele('@data')
        var.set_ele('@data', int(odata))
        var.set_ele('@type_name', '$int$')
        return var
    except e:
        raise Exception('Undefined type conversion')


def to_float(var: ProtoType):
    try:
        odata = var.get_ele('@data')
        var.set_ele('@data', float(odata))
        var.set_ele('@type_name', '$float$')
        return var
    except e:
        raise Exception('Undefined type conversion')


def to_string(var: ProtoType):
    try:
        odata = var.get_ele('@data')
        var.set_ele('@data', str(odata))
        var.set_ele('@type_name', '$string$')
        return var
    except e:
        raise Exception('Undefined type conversion')


__standard_atr = {
    'gt': gt,
    'lt': lt,
    'ge': ge,
    'le': le,
    'eq': eq,
    'plus': plus,
    'minus': minus,
    'times': times,
    'divide': divide,
    'and_operation': and_operation,
    'or_operation': or_operation,
    'not_operation': not_operation,
    'to_int': to_int,
    'to_float': to_float,
    'to_string': to_string
}

ProtoType.class_init(__standard_atr)
